package IC.SemanticChecks;

import IC.AST.ICClass;
import IC.AST.Program;
import IC.SymbolTable.GlobalSymbolTable;
import IC.SymbolTable.SymbolTable;
import IC.Types.TypeTable;

/**
 * Runs all of the semantic checks (designs as a singelton class)
 */
public class SemanticChecks {

	private TablesBuilder Tb;
	private GlobalSymbolTable Gst;
	private final String FileName;
	private final Program program;
	private final ICClass library;
	
	private static SemanticChecks inst = null;
	
	public static boolean HasInstance()
	{
		return (inst!=null);
	}
	
	public static void CreateNewInstance(Program program ,String FileName, ICClass LibRoot) throws Exception
	{
    	if (inst==null)
			inst = new SemanticChecks(program,FileName,LibRoot);
    	else
    		throw new Exception("Instance already exist!");
	}

	public static SemanticChecks Inst() throws Exception
	{
		if (inst==null)
			throw new Exception("Instance is not exist!");
		return inst;
	}
	    
	
	//C'tor
	private SemanticChecks(Program program ,String FileName, ICClass library)  throws SemanticError {
	
		this.FileName = FileName;
		this.program = program;
		this.library = library;
		if(this.library!=null) 
			this.program.getClasses().add(0, this.library);
		this.Tb = new TablesBuilder();
	}
	
	public void Run() throws Exception {
		
		//Check for Library Name:
		if(this.library!=null) 
			if (!this.library.getName().equals("Library")) 
				throw new SemanticError(SemanticErrorType.INVALID_LIBRARY_NAME, library.getLine());
		
		//build tables:
		Gst = (GlobalSymbolTable) this.Tb.Build(program, FileName);
		
		//Run scope checks:
		program.accept(new ScopeCheckingVisitor());
		
		if (SemanticError.hasError())
			SemanticError.throwError();
		
		//Run Type checks:
		program.accept(new TypeCheckingVisitor());
		
		if (SemanticError.hasError())
			SemanticError.throwError();
		
		//Run Main checks:
		program.accept(new MainCheckingVisitor());
		
		if (SemanticError.hasError())
			SemanticError.throwError();
		
		//Run BreakCont checks:
		program.accept(new BreakContCheckingVisitor());
		
		if (SemanticError.hasError())
			SemanticError.throwError();
		
		//Run ReturnInit checks:
		program.accept(new ReturnInitCheckingVisitor(),null);
		
		if (SemanticError.hasError())
			SemanticError.throwError();
		else
			System.out.println(FileName + " passed semantic checks successfully!");
	}
	
	public void printTables() {
		if (Gst != null) {
			printTablesHelper(Gst);
			TypeTable.Print(FileName);
		}
	}
	
	private void printTablesHelper(SymbolTable st) {
		System.out.println((st.toString()));
		for (SymbolTable c : st.getChildren()) printTablesHelper(c);
	}
	
}
